#include "RegFit.h"

RegFit::RegFit()
{
    this->dfa = nullptr;
    this->nfa = nullptr;
}
bool RegFit::parseF(size_t from, size_t to)
{
    if ((this->ptr) != (this->end) && (*(this->ptr)) == '(')
    {
        (this->ptr)++;
        if (!this->parseG(from, to))return false;
        if ((*(this->ptr)) != ')')
            return false;
        (this->ptr)++;
    }
    else 
    {
        while ((this->ptr) != (this->end) && isspace(*(this->ptr)))(this->ptr)++;
        string str = "";
        while ((this->ptr) != (this->end) && isalpha(*(this->ptr)))str+=*((this->ptr)++);
        if (!this->ident_id.count(str))
            return false;
        this->nfa->trans[std::make_pair(from, vector<size_t>({ this->ident_id[str] }))].insert(to);
    }
    return true;
}
bool RegFit::parseM(size_t from, size_t to)
{
    size_t s1 = this->nfa->state_cnt++;
    size_t s2 = this->nfa->state_cnt++;
    if (!this->parseF(s1, s2))return false;
    if ((this->ptr) != (this->end) && (*(this->ptr)) == '*')
    {
        (this->ptr)++;
        this->nfa->trans[std::make_pair(from, vector<size_t>({}))].insert(s1);
        this->nfa->trans[std::make_pair(s2, vector<size_t>({}))].insert(to);
        this->nfa->trans[std::make_pair(from, vector<size_t>({}))].insert(to);
        this->nfa->trans[std::make_pair(s2, vector<size_t>({}))].insert(s1);
    }
    else
    {
        this->nfa->trans[std::make_pair(from, vector<size_t>({}))].insert(s1);
        this->nfa->trans[std::make_pair(s2, vector<size_t>({}))].insert(to);
    }
    return true;
}
bool RegFit::parseT(size_t from, size_t to)
{
    size_t tmp = this->nfa->state_cnt++;
    if (!this->parseM(from,tmp))
        return false;
    from = tmp; 
    while ((this->ptr) != (this->end) && (*(this->ptr)) != ')' && (*(this->ptr)) != '|')
    {
        tmp = this->nfa->state_cnt++;
        if (!this->parseM(from, tmp))
            return false;
        from = tmp;
    }
    this->nfa->trans[std::make_pair(from, vector<size_t>())].insert(to);
    return true;
}
bool RegFit::parseG(size_t from, size_t to)
{
    size_t s1 = this->nfa->state_cnt++;
    size_t s2 = this->nfa->state_cnt++;
    if (!this->parseT(s1, s2))return false;
    this->nfa->trans[std::make_pair(from, vector<size_t>())].insert(s1);
    this->nfa->trans[std::make_pair(s2, vector<size_t>())].insert(to);
    while ((this->ptr) != (this->end) && (*(this->ptr)) == '|')
    {
        (this->ptr)++;
        s1 = this->nfa->state_cnt++;
        s2 = this->nfa->state_cnt++;
        if (!parseT(s1, s2))return false;
        this->nfa->trans[std::make_pair(from, vector<size_t>())].insert(s1);
        this->nfa->trans[std::make_pair(s2, vector<size_t>())].insert(to);
    }
    return true;
}

RegFit::RegFit(const string exp,const map<char,string>mp)
{
    this->dfa = nullptr;
    (this->ptr) = exp.begin();
    this->end = exp.end();
    for (const auto& c : mp)
    {
        if (!this->ident_id.count(c.second))
        {
            for(const auto&s:c.second)
                if (!isalpha(s))
                {
                    *this = RegFit();
                    return;
                }
            this->ident_id[c.second] = this->ident_id.size();
        }
        this->character_id[c.first] = this->ident_id[c.second];
    }
    this->nfa = new NFA();
    bool initial_state = this->nfa->state_cnt++;
    bool final_state = this->nfa->state_cnt++;
    this->nfa->charset_cnt = this->ident_id.size();
    this->nfa->initial_state.insert(initial_state);
    this->nfa->final_state.insert(final_state);
    bool flag = this->parseG(initial_state, final_state);
    if ((this->ptr) != (this->end) || flag == false)
    {
        delete this->nfa;
        *this = RegFit();
        return;
    }
    DFA* dfa_tmp = new DFA();
    NtoD(this->nfa, dfa_tmp);
    delete this->nfa;
    this->nfa = nullptr;
    this->dfa = MinDFA(dfa_tmp);
    delete dfa_tmp;
    dfa_tmp = nullptr;
}

bool RegFit::match(string s)
{
    if (this->dfa == nullptr)return false;
    vector<size_t>es;
    for (const auto& c : s)
        if (!this->character_id.count(c))
            return false;
        else
            es.push_back(this->character_id[c]);
    return this->dfa->match(es);
}
